﻿using System.Collections.Generic;
using TVersityDotNet.HelperTypes;
using TVersityDotNet.Requests;
using TVersityDotNet.Responses;
using System;
using System.Security.Policy;
using System.Collections.ObjectModel;
using System.Globalization;
using TVersityDotNet.MediaLib;
using TVersityDotNet.Status;
using TVersityDotNet.Providers;

[assembly:CLSCompliant(true)]
namespace TVersityDotNet
{
    public class TVersityManager
    {
        private IMessageManager mesManager;
            
        /// <summary>
        /// Constructor for the TversityManager 
        /// </summary>
        /// <param name="tversityBaseUrl">The base URL for the tversity server to control</param>
        public TVersityManager(Uri tversityBaseUrl)
        {
            this.mesManager = new MessageManager(tversityBaseUrl);
        }

		public TVersityManager(Uri tversityBaseUrl, IRestProvider restProvider)
		{
			this.mesManager = new MessageManager(tversityBaseUrl, restProvider);
		}

        /// <summary>
        /// Constructor to inject an IMessageManager
        /// </summary>
        /// <param name="messageManager">Message manager to inject</param>
        public TVersityManager(IMessageManager messageManager)
        {
            this.mesManager = messageManager;
        }

        /// <summary>
        /// Fetch all existing media sources of a given type.
        /// </summary>
        /// <param name="type">The type of the media sources to fetch.</param>
        /// <returns>The list of possible MediaSources</returns>
        public Collection<MediaSource> MediasourceFetchList(MediaSourceType type)
        {
            return ((MediaSourceFetchListResponse)this.mesManager.ProcessRequest(new MediaSourceFetchListRequest(type))).ListOfSources;
        }

        /// <summary>
        /// Update an existing media source.
        /// </summary>
        /// <param name="mediaSource">The media source object to update</param>
        /// <returns>True if succeeds, false if fails</returns>
        public bool MediasourceUpdate(MediaSource mediaSource)
        {
            return this.mesManager.ProcessRequest(new MediaSourceUpdateRequest(mediaSource)).ResponseSuccess;
        }

        /// <summary>
        /// Fetch an integer value that can be compared to previously retrieved values in order to detect changes to the list.
        /// </summary>
        /// <param name="type">The type of the media sources to fetch.</param>
        /// <returns>The integer</returns>
        public int MediasourceFetchListUpdateId(MediaSourceType type)
        {
            return ((MediaSourceFetchListUpdateIdResponse)this.mesManager.ProcessRequest(new MediaSourceFetchListUpdateIdRequest(type))).UpdateId;
        }

        /// <summary>
        /// Fetch all existing media sources of a given type such that if the DB is locked and hence the result cannot be received simply return the same as last time.
        /// </summary>
        /// <param name="type">The type of the media sources to fetch.</param>
        /// <returns>The list of possible MediaSources</returns>
        public Collection<MediaSource> MediasourcePoliteFetchList(MediaSourceType type)
        {
            return ((MediaSourceFetchListResponse)this.mesManager.ProcessRequest(new MediaSourcePoliteFetchListRequest(type))).ListOfSources;
        }

        /// <summary>
        /// Refresh an existing media source.
        /// </summary>
        /// <param name="mediaSource">The MediaSource to refresh</param>
        /// <returns>True if the refresh succeeded, False if it failed</returns>
        public bool MediasourceRefresh(MediaSource mediaSource)
        {
            return (this.mesManager.ProcessRequest(new MediasourceRefreshRequest(mediaSource))).ResponseSuccess;
        }

        /// <summary>
        /// Refresh an existing media source.
        /// </summary>
        /// <param name="mediaSourceId">The Id of the MediaSource to refresh</param>
        /// <returns>True if the refresh succeeded, False if it failed</returns>
        public bool MediasourceRefresh(int mediaSourceId)
        {
            MediaSource ms = new MediaSource(mediaSourceId, string.Empty, MediaSourceType.BadType, string.Empty, new Collection<string>(), true);
            return (this.mesManager.ProcessRequest(new MediasourceRefreshRequest(ms))).ResponseSuccess;
        }

        /// <summary>
        /// Clean the cache area
        /// </summary>
        /// <returns>True if the cache delete succeeded, False if it failed</returns>
        public bool CacheDeleteAll()
        {
            return (this.mesManager.ProcessRequest(new CacheDeleteAllRequest())).ResponseSuccess;
        }

        /// <summary>
        /// Start the Tversity service
        /// </summary>
        /// <returns>True if the service started correctly, False if it failed</returns>
        public bool StartService()
        {
            return (this.mesManager.ProcessRequest(new ServiceRestartRequest())).ResponseSuccess;
        }

        /// <summary>
        /// Restart the Tversity service
        /// </summary>
        /// <returns>True if the service restarted correctly, False if it failed</returns>
        public bool RestartService()
        {
            return (this.mesManager.ProcessRequest(new ServiceRestartRequest())).ResponseSuccess;
        }

        /// <summary>
        /// Restart the Tversity service
        /// </summary>
        /// <returns>True if the service restarted correctly, False if it failed</returns>
        public bool StopService()
        {
            return (this.mesManager.ProcessRequest(new ServiceRestartRequest())).ResponseSuccess;
        }

        /// <summary>
        /// Refresh all media sources
        /// </summary>
        /// <returns>True if succeeded, False if failed</returns>
        public bool MediasourceRefreshAll()
        {
            return (this.mesManager.ProcessRequest(new MediasourceRefreshAllRequest())).ResponseSuccess;
        }

        /// <summary>
        /// Reconstruct the media library (this is the same as adding all the media sources to an empty library).
        /// </summary>
        /// <returns>True if succeeded, False if failed</returns>
        public bool MediasourceResetAll()
        {
            return (this.mesManager.ProcessRequest(new MediasourceRefreshAllRequest())).ResponseSuccess;
        }

        /// <summary>
        /// Delete an existing media source.
        /// </summary>
        /// <param name="id">The Id of the mediasource to delete</param>
        /// <returns>True if succeeded, False if failed</returns>
        public bool MediasourceDelete(int mediaSourceId)
        {
            return (this.mesManager.ProcessRequest(new MediaSourceDeleteRequest(mediaSourceId))).ResponseSuccess;
        }

        /// <summary>
        /// Delete an existing media source.
        /// </summary>
        /// <param name="mediaSource">The MediaSource to delete</param>
        /// <returns>True if succeeded, False if failed</returns>
        public bool MediasourceDelete(MediaSource mediaSource)
        {
            if (mediaSource != null)
                return this.MediasourceDelete(mediaSource.Id);
            else
                return false;
        }

        /// <summary>
        /// Delete an existing media source.
        /// </summary>
        /// <param name="mediaSource">The MediaSource to delete</param>
        /// <returns>True if succeeded, False if failed</returns>
        public MediaSource MediasourceFetch(int mediaSourceId)
        {
            return ((MediaSourceFetchResponse)this.mesManager.ProcessRequest(new MediaSourceFetchRequest(mediaSourceId))).MediaSource;
        }
        
        /// <summary>
        /// Proxy a TVersityRequest
        /// </summary>
        /// <param name="request">The TVersityRequest to proxy</param>        
        /// <returns>The TVersityRequest's response</returns>
        public TVersityResponse Proxy(TVersityRequest request)
        {
            return this.mesManager.ProcessRequest(new ProxyRequest(request, this.mesManager.BaseUrl));
        }

        /// <summary>
        /// Fetch the UPnP device description document.
        /// </summary>
        /// <returns>An Description documnt. In this document thee is a list of the services implemented by the device and a URL from which the service description can be fetched. These service are based on SOAP and together form a full implementation of the UPnP A/V Media Server device and its services.</returns>
        public DescriptionFetchResponse DescriptionFetch()
        {
            return (DescriptionFetchResponse)this.mesManager.ProcessRequest(new DescriptionFetchRequest());            
        }

        /// <summary>
        /// Fetch the current server settings
        /// </summary>        
        /// <returns></returns>
        public SettingsFetchResponse SettingsFetch()
        {
            return (SettingsFetchResponse)this.mesManager.ProcessRequest(new SettingsFetchRequest());
        }

        /// <summary>
        /// Fetch the current server settings
        /// </summary>
        /// <param name="updateBaseUrlOnPortChange">Whether or not to update the TVersityManager's BaseUrl if a port change is detected</param>
        /// <returns></returns>
        public TVersityResponse SettingsUpdate(TVSettings settings, bool updateBaseUrlOnPortChange)
        {
            TVersityResponse response;
            if (settings != null)
            {
                response = this.mesManager.ProcessRequest(new SettingsUpdateRequest(settings));

                if (settings.PropertyBag.ContainsKey("ListenPort", true) && response.ResponseSuccess && updateBaseUrlOnPortChange)
                {
                    string newUri = this.mesManager.BaseUrl.AbsoluteUri.Replace(":" + this.mesManager.BaseUrl.Port.ToString(CultureInfo.InvariantCulture), (string)settings.PropertyBag.GetPropertyFromPropertyBag("ListenPort"));
                    this.mesManager.BaseUrl = new Uri(newUri);
                }
            }
            else
            {
                response = new TVersityResponse();
                response.ResponseSuccess = false;
                response.ResponseMessage = "Provided TVSettings was null. Can't generate proper request";
            }

            return response;
        }

        /// <summary>
        /// Add a new media source to the library.
        /// </summary>
        /// <param name="mediaSource">The media source to add</param>
        /// <returns>True if request succeeds, false if it fails</returns>
        public int MediasourceAdd(MediaSource mediaSource)
        {
            return ((MediaSourceAddResponse)this.mesManager.ProcessRequest(new MediaSourceAddRequest(mediaSource))).MediaSourceId;
        }

        /// <summary>
        /// Browse the media library menu hierarchy and receive results in a specified format
        /// </summary>
        /// <param name="browseOptions">All the option parameters to use in query</param>
        /// <returns>The results of the browse query</returns>
        public MediaLibResponse MedialibBrowse(BrowseOptions browseOptions)
        {            
            return (MediaLibResponse)this.mesManager.ProcessRequest(new BrowseRequest(browseOptions));
        }

        /// <summary>
        /// Search the media library and receive results in a specified format
        /// </summary>
        /// <param name="searchOptions">All the option parameters to use in query</param>
        /// <returns>The results of the browse query</returns>
        public MediaLibResponse MedialibSearch(SearchOptions searchOptions)
        {
            return (MediaLibResponse)this.mesManager.ProcessRequest(new SearchRequest(searchOptions));            
        }

        /// <summary>
        /// Fetch the current server status, which includes list of devices detected during runtime, list of transcode sessions, streaming sessions and download sessions. It also includes a list of server messages created in response to some background operation.
        /// </summary>
        /// <returns>The current server's status</returns>
        public TVStatus StatusFetch()
        {
            return ((StatusFetchResponse)this.mesManager.ProcessRequest(new StatusFetchRequest())).Status;
        }

		public Uri BaseUrl
		{
			get { return this.mesManager.BaseUrl; }
		}
    }
}
